
"""
<a href="http://en.wikipedia.org/wiki/Intel_HEX">Intel HEX</a> is a format to \
represent the binary information in ASCII. It's commonly used for programming \
with the address.

For each of the line, it's the definition:

<pre>
: 0 1 2 3 4 5 7 8 ... 9 A
- --- ------- --- --- ---
|  |     |     |   |   |
|  |     |     |   |   `- Checksum
|  |     |     |   `- Data Bytes
|  |     |     `- Record Type
|  |     `- Address
|  `- Byte Count
`- Start Byte ':'
</pre>

The record type decides the meaning of the line:

<ul>
  <li><code>00</code> - data Record</li>
  <li><code>01</code> - End of file Record, usually it's <code>:00000001FF \
      </code></li>
  <li><code>02</code> - extended segment address record. this indicates \
      segment base address when 16 bits is not enough for addressing memory
      </li>
  <li><code>03</code> - start segment address record. indicates initial \
      segment base address</li>
  <li><code>04</code> - extended linear address record - allows 32 bit \
      address </li>
  <li><code>05</code> - start linear address record</li>
</ul>

More detail can refer to the sepcification: \
<a href="http://microsym.com/editor/assets/intelhex.pdf">
  Intel Hexadecimal Object File Format Specification Rev A, 1/6/88</a>
"""

__version__ = "1.09"

__aof__ = ('Intel Hex', 'I32')
__extension__ = (('Intel Hex Files', ('*.i32', '*.hex')),)

_CHECK_CHECKSUM = 'Check I32 Checksum'

__configs__ = (
    {'name': _CHECK_CHECKSUM,
     'attr': 'bool',
     'default': False},
)


def _si(value):
    try:
        return int(value, 16)
    except ValueError:
        return -1


class _IntelHexDecoder:
    def Execute(self, source, output, notify, **kws):
        ret = False

        start = kws.get('start', 0)
        size = length = kws.get('length', len(source)) - start
        if length < 0:
            length = 0

        base = 0
        chksum = False
        if '_i' in kws:
            configs = kws['_i'].configs()
            chksum = configs.get(_CHECK_CHECKSUM, chksum)

        source.seek(start)
        while length > 0:
            ret = False
            dat = ''
            while length > 0:
                c = source.read(1)
                length -= 1
                if c == '\r' or c == '\n':
                    break
                else:
                    dat += c

            if int(10 * length / size) == 10 * length / size:
                notify(100 * (size - length) / size, 'Parse i32 data ...')

            li = dat.strip()
            if len(li) == 0:
                continue
            elif len(li) < 11:
                # the minimum line should be: :00000001FF
                break
            elif li[0] != ':':
                break

            count = _si(li[1:3])
            # address = _si(li[3:7])
            tp = _si(li[7:9])
            # check the checksum
            if chksum:
                checksum = 0
                for i in xrange(len(li) / 2):
                    o = 1 + 2 * i
                    checksum += _si(li[o:o + 2])

                if (checksum & 0xff) != 0:
                    break

            ret = True
            if tp == 2 or tp == 4:
                # extended linear address
                base = 0
                for k in range(9, 9 + count * 2, 2):
                    base = base * 256 + _si(li[k:k + 2])
                base *= 2 ** (4 * tp)
            elif tp == 0:
                # data record
                o = ''
                for k in range(9, 9 + count * 2, 2):
                    o += chr(_si(li[k:k+2]))

                output.seek(base)
                output.write(o)
                base += len(o)
            elif tp == 1:
                # end of the file record
                break

        return ret


def cvtService():
    return _IntelHexDecoder
